<!-- 
  * 地区选择框
  * 
  * @Author:    zhoujk
  * @Date:      2025-02-06 10:46
  *
-->

<template>
  <a-cascader
    :style="`width:${width}`"
    v-model:value="areaValue"
    :field-names="{ label: 'name', value: 'code', children: 'children' }"
    :options="options"
    :load-data="loadData"
    :placeholder="placeholder"
    :size="size"
    @change="handleChange"
    change-on-select
  >
    <template #tagRender="data">
      <a-tag :key="data.code" color="blue">{{ data.name }}</a-tag>
    </template>
  </a-cascader>
</template>

<script setup>
  import { onMounted, ref, toRaw, watch } from 'vue'
  import { areaApi } from '/@/api/business/b-operation/area/area-api.js'

  // ============ 组件属性 ============

  const props = defineProps({
    type: String,
    value: [Number, Array, String],
    width: {
      type: String,
      default: '100%',
    },
    placeholder: {
      type: String,
      default: '请选择地区',
    },
    // 请求参数
    queryParams: {
      type: Object,
      default: () => ({
        fAreaId: '000000000000',
        type: 1,
      }),
    },
    // 是否返回多个值
    getMultiple: {
      type: Boolean,
      default: false,
    },
    // 选择层级深度
    depth: {
      type: Number,
      default: 4,
    },
    size: {
      type: String,
      default: 'middle',
      validate: (value) => ['small', 'medium', 'large'].includes(value),
    },
    disabled: {
      type: Boolean,
      default: false,
    },
  })

  const options = ref([])
  const areaValue = ref([])

  const emit = defineEmits(['update:value', 'change'])

  const loadData = async (selectedOptions) => {
    const targetOption = selectedOptions[selectedOptions.length - 1]
    targetOption.loading = true

    let type = targetOption.type + 1 <= props.depth - 1 ? targetOption.type + 1 : props.depth - 1

    // 请求后端接口
    const res = await areaApi.page({ fAreaId: targetOption.id, pageNo: 1, pageSize: 100, type: type })

    targetOption.children = targetOption.type + 1 !== props.depth - 1 ? res.data.map((item) => ({ ...item, isLeaf: false })) : res.data

    options.value = [...options.value]
    targetOption.loading = false
  }

  // 初始化时加载数据
  onMounted(async () => {
    const res = await areaApi.page({ fAreaId: props.queryParams.fAreaId, pageNo: 1, pageSize: 100, type: props.queryParams.type })

    options.value = res.data.map((item) => ({
      ...item, // 保留原有字段
      isLeaf: false, // 新增字段
    }))

    // 如果回显值存在，递归加载完整链路
    if (props.value && props.value.length > 0) {
      await selected(props.value)
    }
  })

  const selected = async (code) => {
    // 缓存当前 selectedValue
    const res = await areaApi.findList({ code: code, type: props.queryParams.type })

    const selectedPath = res.data.sort((a, b) => a.type - b.type)

    // 逐层加载子节点
    await loadPath(selectedPath)
  }

  // 递归加载选中路径
  const loadPath = async (selectedOptions) => {
    let currentOptions = options.value
    const loadedOptions = []

    for (const selectedOption of selectedOptions) {
      const targetOption = currentOptions.find((opt) => opt.code === selectedOption.code)
      if (!targetOption) break

      loadedOptions.push(targetOption)

      // 如果是最后一个值则不需要继续加载
      if (loadedOptions.length === selectedOptions.length) break

      // 如果还没有子节点数据，则触发加载
      if (!targetOption.children || targetOption.children.length === 0) {
        await loadData([...loadedOptions])
      }

      // 更新当前选项为子节点列表
      if (targetOption.children) {
        currentOptions = targetOption.children
      } else {
        break
      }
    }

    areaValue.value = selectedOptions.map((item) => item.code)
  }

  // ============ 组件业务 ============

  function updateSelectedValue(newValue) {
    if (newValue) {
      if (Array.isArray(newValue)) {
        selected(newValue[newValue.length - 1])
      } else {
        selected(newValue)
      }
    } else {
      areaValue.value = []
    }
  }

  function handleChange(value, selectedOptions) {
    // 如果启用了多选，返回整个选中的数组
    if (props.getMultiple) {
      emit('update:value', value)
    } else {
      // 否则返回最后一个选中的值
      emit('update:value', selectedOptions[selectedOptions.length - 1].code)
    }
    emit('change', value, toRaw(selectedOptions))
  }
  defineExpose({
    updateSelectedValue,
  })
</script>
